package test

import (
	"fmt"
	"log"
	"strconv"
	"time"

	"nosqlx/consulx"

	"github.com/hashicorp/consul/api"
	"github.com/onsi/ginkgo/v2"
	"github.com/onsi/gomega"
)

var _ = ginkgo.Describe("kv", ginkgo.Ordered, func() {
	var clientx *api.Client
	ginkgo.BeforeAll(func() {
		client, err := consulx.ConsulClient()
		gomega.Expect(err).To(gomega.BeNil())
		clientx = client

	})

	ginkgo.AfterAll(func() {

		kv := clientx.KV()
		tree, err := kv.DeleteTree("", nil)
		gomega.Expect(err).To(gomega.BeNil())
		gomega.Expect(tree.RequestTime).NotTo(gomega.BeZero())

	})
	var _ = ginkgo.Describe("kv", func() {
		invalidKey := "invalidKey"

		ginkgo.It("basic put/get", func() {
			kv := clientx.KV()
			pair := api.KVPair{
				Key:         invalidKey,
				CreateIndex: 0,
				ModifyIndex: 0,
				LockIndex:   0,
				Flags:       42,
				Value:       []byte(invalidKey + ":value"),
				Session:     "",
				Namespace:   "ns",
				Partition:   "",
			}
			w, err := kv.Put(&pair, nil)
			gomega.Expect(err).To(gomega.BeNil())
			gomega.Expect(w.RequestTime).NotTo(gomega.BeZero())

			kvPair, queryMeta, err := kv.Get(invalidKey, nil)
			gomega.Expect(err).To(gomega.BeNil())

			gomega.Expect(kvPair.Key).To(gomega.Equal(pair.Key))
			gomega.Expect(kvPair.Value).To(gomega.Equal(pair.Value))
			//Enterprise
			//gomega.Expect(kvPair.Namespace).Should(gomega.Equal(pair.Namespace))

			fmt.Println(queryMeta)

		})

		ginkgo.It("Enterprise feature", func() {
			kv := clientx.KV()
			invalidKey := "invalidKey:"
			for i := 0; i < 10; i++ {
				key := invalidKey + strconv.Itoa(i)
				pair := api.KVPair{
					Key:         key,
					CreateIndex: 0,
					ModifyIndex: 0,
					LockIndex:   0,
					Flags:       42,
					Value:       []byte(key + ":value"),
					Session:     "",
					Namespace:   "",
					Partition:   "",
				}
				w, err := kv.Put(&pair, &api.WriteOptions{
					//Unexpected response code: 400 (Invalid query parameter: "ns" - Namespaces are a Consul Enterprise feature)
					Namespace:   "",
					Partition:   "",
					Datacenter:  "",
					Token:       "",
					RelayFactor: 0,
				})
				gomega.Expect(err).To(gomega.BeNil())
				gomega.Expect(w.RequestTime).NotTo(gomega.BeZero())

				kvPair, _, err := kv.Get(key, nil)
				gomega.Expect(err).Should(gomega.BeNil())

				gomega.Expect(kvPair.Key).To(gomega.Equal(pair.Key))
				gomega.Expect(kvPair.Value).To(gomega.Equal(pair.Value))
			}

		})

		ginkgo.It("list", func() {
			kv := clientx.KV()
			list, q, err := kv.List("", &api.QueryOptions{
				Namespace:          "",
				Partition:          "",
				SamenessGroup:      "",
				Datacenter:         "",
				Peer:               "",
				AllowStale:         false,
				RequireConsistent:  false,
				UseCache:           false,
				MaxAge:             0,
				StaleIfError:       0,
				WaitIndex:          0,
				WaitHash:           "",
				WaitTime:           0,
				Token:              "",
				Near:               "",
				NodeMeta:           nil,
				RelayFactor:        0,
				LocalOnly:          false,
				Connect:            false,
				Filter:             "",
				MergeCentralConfig: false,
				Global:             false,
			})
			gomega.Expect(err).To(gomega.BeNil())
			gomega.Expect(len(list)).To(gomega.Equal(11))
			fmt.Println(q)

		})

		ginkgo.It("cas", func() {
			kv := clientx.KV()
			kvPair, _, err := kv.Get("invalidKey", nil)
			gomega.Expect(err).To(gomega.BeNil())

			pair := api.KVPair{
				Key:   invalidKey,
				Value: []byte(invalidKey + ":CAS"),
			}
			cas, _, err := kv.CAS(&pair, nil) // 如果 ModifyIndex 一致才更新
			gomega.Expect(err).To(gomega.BeNil())
			gomega.Expect(cas).To(gomega.Equal(false))

			pair.ModifyIndex = kvPair.ModifyIndex
			cas, _, err = kv.CAS(&pair, nil) // 如果 ModifyIndex 一致才更新
			gomega.Expect(err).To(gomega.BeNil())
			gomega.Expect(cas).To(gomega.Equal(true))
		})

		ginkgo.It("watch", func() {
			kv := clientx.KV()

			//meta.LastIndex	本次返回数据时的最新变更索引（下次用它作为 WaitIndex）
			//pair.ModifyIndex	当前这个 key 的最后修改索引
			times := 5
			periodTime := 200 * time.Millisecond

			go func() {
				for i := 0; i < times; i++ {
					value := invalidKey + strconv.Itoa(i) + ":value"
					pair := api.KVPair{
						Key:   invalidKey,
						Value: []byte(value),
					}
					w, err := kv.Put(&pair, nil)
					gomega.Expect(err).To(gomega.BeNil())
					gomega.Expect(w.RequestTime).NotTo(gomega.BeZero())
					time.Sleep(periodTime)
				}
			}()

			var lastIndex uint64 = 0
			for i := 0; i < times; i++ {
				now := time.Now()
				kvPair, meta, err := kv.Get(invalidKey, &api.QueryOptions{
					WaitIndex: lastIndex,
					// 避免永遠阻塞
					WaitTime: periodTime,
				})
				gomega.Expect(err).To(gomega.BeNil())
				log.Println(time.Since(now), kvPair.Key, string(kvPair.Value), meta.LastIndex, kvPair.ModifyIndex, kvPair.CreateIndex)
				lastIndex = meta.LastIndex
			}
		})
	})

})
